/**
 * 
 */
package org.terukusu.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.http.Header;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

/**
 * Network Utilies
 * 
 * @author Teruhiko Kusunoki&lt;<a href="teru.kusu@gmail.com">teru.kusu@gmail.com</a>&gt;
 *
 */
public class NetUtils {
	
	/**
	 * HTTPのGETメソッドでデータを送受信します。
	 * 今のところクッキーの送信には非対応。
	 * 
	 * @param urlStr URL
	 * @param params パラメータ
	 * @param headers 追加のHTTPヘッダ
	 * @param cookies クッキー
	 * @return 取得したデータ
	 * @throws IOException 入出力に関する問題が発生した場合。
	 */
	public static HttpResponse httpGet(String urlStr, Map<String, String> params, Map<String, String> headers, Map<String, String> cookies, boolean isPost) throws IOException {
		HttpRequestBase request = null;

		List<NameValuePair> postParams = new ArrayList<NameValuePair>();

		if (isPost) {
			HttpPost post = new HttpPost(urlStr);
			if (params != null) {
				for (Map.Entry<String, String> entry : params.entrySet()) {
					postParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
				}
			}
		    post.setEntity(new UrlEncodedFormEntity(postParams, HTTP.UTF_8));

		    request = post;
		} else {
			StringBuilder sb = new StringBuilder(urlStr);
			if (params != null) {
				if (urlStr.contains("?")) {
					sb.append('&');
				} else {
					sb.append('?');
				}
				sb.append(makeQueryString(params));
			}
			String fullUrl = sb.toString();
			
			HttpGet get = new HttpGet(fullUrl);
			
			request = get;
		}

		HttpClient client = new DefaultHttpClient();
		org.apache.http.HttpResponse hr = null;
		HttpResponse res = new HttpResponse();
		try {
		    hr = client.execute(request);

		    // body
		    ByteArrayOutputStream baos = null;
			InputStream is = null;
			try {
				baos = new ByteArrayOutputStream();
				is = hr.getEntity().getContent();
				byte[] buff = new byte[8192];
			    // TODO 読み込み上限を設定
				for (int i = 0; ((i = is.read(buff)) != -1); ) {
					baos.write(buff, 0 , i);
				}
				
			} finally {
				try { baos.close(); } catch (Exception ignore) {}
				try { is.close(); } catch (Exception ignore) {}
			}
		    
		    res.setBody(baos.toByteArray());
		    
		    // header
		    // TODO 重複キーに対応
		    for (Header header : hr.getAllHeaders()) {
		    	res.setHeader(header.getName(), header.getValue());
		    }
		    
		    // cookie
		    String key = null;
		    String value = null;
		    int equalIndex;
		    for (Header header : hr.getHeaders("Set-Cookie")) {
		    	String cookieEntry = header.getValue();
				key = cookieEntry;
				value = "";
				
				equalIndex = cookieEntry.indexOf('=');
				if (equalIndex >= 0) {
					key = cookieEntry.substring(0, equalIndex);
					value = cookieEntry.substring(equalIndex + 1);
					if (value.indexOf(';') >= 0) {
						value = value.substring(0, value.indexOf(';'));
					}
				}

				res.setCookie(key, value);
		    }
		} catch (ClientProtocolException e) {
		    throw new RuntimeException(e);
		} catch (IOException e) {
		    throw new RuntimeException(e);
		}
		
		return res;
	}
	
	private static String makeQueryString(Map<String, String> params) {
		StringBuilder sb = new StringBuilder();
		for (Map.Entry<String, String> entry : params.entrySet()) {
			if (sb.length() > 0) {
				sb.append("&");
			}
			try {
				sb.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), "UTF-8"));
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
		}

		return sb.toString();
	}
}
